package org.millioncall.yueyoga.admin.service.stat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.millioncall.yueyoga.common.SystemException;

/**
 * 分组链。注意：横向及纵向树，必须满足，任一分支深度相同，否则无法创建表体。
 * 
 * @author zangjinyu
 * @since 1.0.0
 * @version 1.0.0
 * 
 */
public class GroupByTableBuilder {
	private List<GroupByTreeNode<GroupByNodeInfo>> leftGroupByRootNodes = new ArrayList<GroupByTreeNode<GroupByNodeInfo>>();// 无根节点，横向分组
	private List<GroupByTreeNode<GroupByNodeInfo>> topGroupByRootNodes = new ArrayList<GroupByTreeNode<GroupByNodeInfo>>();// 无根节点，纵向分组
	private int leftDepth;// 横向树深度
	private int topDepth;// 纵向树深度
	private List<Object[]> results;// 分组查询结果

	private int leftGroupByCount;
	private int topGroupByCount;

	private Map<String, Integer> tdTextPositionMap = new HashMap<String, Integer>();// 存储横向分组节点和纵向分组节点和位置的映射

	/**
	 * 
	 * @param results
	 * @param leftGroupByCount
	 *            横向分组列数
	 * @param topGroupByCount
	 *            纵向分组列数
	 */
	public GroupByTableBuilder(List<Object[]> results, int leftGroupByCount,
			int topGroupByCount) {
		if (results == null || results.isEmpty()) {
			throw new SystemException("查询结果不可为空");
		}
		this.results = results;

		if (leftGroupByCount < 0 || topGroupByCount < 0) {
			throw new SystemException("横向或纵向分组数不能小于0");
		}

		this.leftGroupByCount = leftGroupByCount;
		this.topGroupByCount = topGroupByCount;

		buildGroupByTreeNode();

		this.leftDepth = this.leftGroupByRootNodes.isEmpty() ? 0
				: this.leftGroupByRootNodes.get(0).getDepth();
		this.topDepth = this.topGroupByRootNodes.isEmpty() ? 0
				: this.topGroupByRootNodes.get(0).getDepth();
	}

	private void buildGroupByTreeNode() {
		Map<String, GroupByTreeNode<GroupByNodeInfo>> map = new HashMap<String, GroupByTreeNode<GroupByNodeInfo>>();
		// 左侧和上班的根节点

		for (Object[] arr : this.results) {
			String keyChain = "h_";
			for (int i = 0; i < this.leftGroupByCount; i++) {// 遍历横向
				GroupByTreeNode<GroupByNodeInfo> parentNode = null;
				if (i > 0) {
					parentNode = map.get(keyChain);
				}

				keyChain += arr[i] == null ? "-" : arr[i].toString() + ",";
				GroupByTreeNode<GroupByNodeInfo> node = map.get(keyChain);
				if (node == null) {
					node = new GroupByTreeNode<GroupByNodeInfo>();
					node.setText(arr[i] == null ? "-" : arr[i].toString());
					if (parentNode != null) {
						node.setParentNode(parentNode);
						parentNode.addChildNode(node);
					} else {// 本身为根节点
						this.leftGroupByRootNodes.add(node);
					}
					map.put(keyChain, node);
				}
			}

			keyChain = "v_";
			for (int i = this.leftGroupByCount; i < this.leftGroupByCount
					+ this.topGroupByCount; i++) {// 遍历纵向
				GroupByTreeNode<GroupByNodeInfo> parentNode = null;
				if (i - this.leftGroupByCount > 0) {
					parentNode = map.get(keyChain);
				}

				keyChain += arr[i] == null ? "-" : arr[i].toString() + ",";

				GroupByTreeNode<GroupByNodeInfo> node = map.get(keyChain);
				if (node == null) {
					node = new GroupByTreeNode<GroupByNodeInfo>();
					node.setText(arr[i] == null ? "-" : arr[i].toString());
					if (parentNode != null) {
						node.setParentNode(parentNode);
						parentNode.addChildNode(node);
					} else {// 本身为根节点
						this.topGroupByRootNodes.add(node);
					}
					map.put(keyChain, node);
				}
			}
		}
	}

	/**
	 * 按分组条件生成表格体
	 * 
	 * @return
	 */
	public TableView buildTable() {
		TableView tv = new TableView();
		// 纵向分组的行数取决于纵向分组树的高度
		int headerRows = this.topDepth == 0 ? 1 : this.topDepth;

		// 横向分组的行数取决于横向分组的总结点数
		int bodyRows = 0;
		for (GroupByTreeNode<GroupByNodeInfo> node : this.leftGroupByRootNodes) {
			bodyRows += node.countOfTotalNodes();
		}

		if (bodyRows == 0) {
			bodyRows = 1;
		}

		int totalCols = 1;// 总列数，取决于纵向分组的叶子节点数
		for (GroupByTreeNode<GroupByNodeInfo> node : this.topGroupByRootNodes) {
			totalCols += node.countOfLeafNodes() == 0 ? 1 : node
					.countOfLeafNodes();// 注意：当根节点无叶子节点的时候，则根节点计为一个叶子节点
		}
		if (totalCols == 1) {
			totalCols = 2;
		}

		// 左上单元格
		Td td1 = new Td("");
		td1.setRowspan(headerRows);

		// 生成所有头部tr
		List<Tr> headerTrs = new ArrayList<Tr>(headerRows);
		for (int i = 0; i < headerRows; i++) {
			Tr tr = new Tr();
			if (i == 0) {
				tr.addTd(td1);
			}

			int tdIndex = 1;// 从1开始，左侧固定一列为行分组
			for (GroupByTreeNode<GroupByNodeInfo> node : this.topGroupByRootNodes) {// 纵向分组树
				List<GroupByTreeNode<GroupByNodeInfo>> depthn = node
						.getTreeNodesOfDepth(i + 1);// 获取第n层的所有节点
				for (GroupByTreeNode<GroupByNodeInfo> noden : depthn) {// 遍历第n层
					Td tdn = new Td(noden.getText());// 此处使用的是节点文本，当然也可以使用GroupByTreeNodeData中的数据

					if (i == headerRows - 1) {// 最后一行，记录列位置
						tdTextPositionMap.put(
								"v_" + noden.getTextChainFromRoot(), tdIndex);
						tdIndex++;
					}

					if (noden.countOfLeafNodes() > 1) {// 列合并，是叶子节点数
						tdn.setColspan(noden.countOfLeafNodes());
					}
					tr.addTd(tdn);
				}
			}
			if (i == 0 && tr.getTds().size() == 1) {
				tr.addTd(new Td("统计结果"));
			}
			headerTrs.add(tr);
		}
		tv.setHeader(headerTrs);

		// 生成所有表体tr
		List<Tr> bodyTrs = new ArrayList<Tr>(bodyRows);
		for (GroupByTreeNode<GroupByNodeInfo> node : this.leftGroupByRootNodes) {
			createTrByTreeNode(node, bodyTrs, totalCols, 0);
		}

		if (bodyTrs.size() == 0) {// 表示无横向分组
			Tr tr = new Tr();
			tr.addTd(new Td("统计结果"));
			for (int i = 0; i < totalCols - 1; i++) {
				tr.addTd(new Td("-"));
			}
			bodyTrs.add(tr);
		} else {
			createTotalRow(bodyTrs, totalCols);// 添加一个小计行
		}
		tv.setBody(bodyTrs);

		// 将统计的真正查询结果数据填入到表格中
		for (Object[] arr : this.results) {
			List<Object> record = Arrays.asList(arr);

			// 先行后列
			List<Object> leftKeyChain = record.subList(0, this.leftDepth);
			List<Object> topKeyChain = record.subList(this.leftDepth,
					this.leftDepth + this.topDepth);

			// 值
			Object value = record.get(this.topDepth + this.leftDepth);
			if (value == null) {
				value = "-";
			}

			int row = getPositionByGroupChain("h", leftKeyChain);
			int col = getPositionByGroupChain("v", topKeyChain);

			if (row == -100) {
				row = bodyTrs.size() - 1;
			}

			if (col == -100) {
				col = 1;
			}

			Td target = bodyTrs.get(row).getTds().get(col);
			target.setText(value.toString());// 将值设置到单元格中

			if (value instanceof Number) {// 如果本次值为数字
				boolean isRuning = true;
				while (isRuning && leftKeyChain.size() > 0) {// 设置父级的值
					int parentRow = -1;
					if (leftKeyChain.size() == 1) {
						parentRow = tdTextPositionMap.get("total_row");
						isRuning = false;
					} else {
						leftKeyChain = leftKeyChain.subList(0,
								leftKeyChain.size() - 1);
						parentRow = getPositionByGroupChain("h", leftKeyChain);
					}

					String original = bodyTrs.get(parentRow).getTds().get(col)
							.getText();// 父级节点原值

					if (StringUtils.isNoneBlank(original)) {// 如何原值不为空，通常为"-"
						try {
							double d = Double.parseDouble(original);
							Number num = (Number) value;
							bodyTrs.get(parentRow)
									.getTds()
									.get(col)
									.setText(
											String.valueOf(d
													+ num.doubleValue()));
						} catch (NumberFormatException e) {
							bodyTrs.get(parentRow).getTds().get(col)
									.setText(value.toString());
						}
					} else {// 如果原值为空
						bodyTrs.get(parentRow).getTds().get(col)
								.setText(value.toString());
					}
				}
			}
		}

		// 添加一个总计行
		Tr lastTr = bodyTrs.get(bodyTrs.size() - 1);// 最后一行
		double total = 0;
		for (int i = 1; i < lastTr.getTds().size(); i++) {
			Td tmp = lastTr.getTds().get(i);
			if (!"-".equals(tmp.getText())) {
				total += Double.valueOf(tmp.getText());
			}
		}

		Tr tr = new Tr();
		Td td = new Td("总计");
		tr.addTd(td);

		td = new Td(String.valueOf(total));
		td.setColspan(totalCols - 1);
		tr.addTd(td);

		bodyTrs.add(tr);

		return tv;
	}

	/**
	 * 创建最后一行小计行
	 * 
	 * @param rows
	 * @param totalCols
	 */
	private void createTotalRow(List<Tr> rows, int totalCols) {
		Tr tr = new Tr();
		Td td = new Td("小计");
		tr.addTd(td);
		for (int i = 0; i < totalCols - 1; i++) {
			tr.addTd(new Td("-"));
		}
		rows.add(tr);
		tdTextPositionMap.put("total_row", rows.size() - 1);// 分组字段内容和位置的对应，此位置不包含表头行数
	}

	/**
	 * 根据横向分组树创建Tr
	 * 
	 * @param node
	 * @param totalCols
	 * @return
	 */
	private void createTrByTreeNode(GroupByTreeNode<GroupByNodeInfo> node,
			List<Tr> rows, int totalCols, int countOfPrefix) {
		Tr tr = new Tr();
		String text = node.getText();
		for (int i = 0; i < countOfPrefix; i++) {
			text = "&nbsp;&nbsp;&nbsp;&nbsp;" + text;
		}
		Td td = new Td(text);

		tr.addTd(td);
		for (int i = 0; i < totalCols - 1; i++) {
			tr.addTd(new Td("-"));
		}
		rows.add(tr);

		tdTextPositionMap.put("h_" + node.getTextChainFromRoot(),
				rows.size() - 1);// 分组字段内容和位置的对应，此位置不包含表头行数

		if (node.getChildNodes().size() > 0) {
			for (GroupByTreeNode<GroupByNodeInfo> childNode : node
					.getChildNodes()) {
				createTrByTreeNode(childNode, rows, totalCols,
						countOfPrefix + 1);
			}
		}
	}

	/**
	 * 根据一个分组结果查询其值所在位置，行或列
	 * 
	 * @param groupByChain
	 * @return
	 */
	private int getPositionByGroupChain(String direction,
			List<Object> groupByChain) {
		if (groupByChain == null || groupByChain.isEmpty()) {
			return -100;
		}
		StringBuilder sb = new StringBuilder();
		for (Object obj : groupByChain) {
			sb.append(obj == null ? "-" : obj.toString());
			sb.append(",");
		}
		String chain = sb.toString();
		if (StringUtils.isNoneBlank(chain)) {
			chain = chain.substring(0, chain.length() - 1);
		}
		return tdTextPositionMap.get(direction + "_" + chain);
	}
}
